#include "VectorModeler.h"
#include "DocVector.h"
#include "MD5toURL.h"
#include "PositionModeler.h"
#include <math.h>
#include <map>
#include <iostream>

using namespace std;

VectorModeler::VectorModeler()
{
}

VectorModeler::~VectorModeler()
{
}

double VectorModeler::dotProduct(DocVector* v1, DocVector* v2)
{
	double result =0;
	int last = min(v1->getDimensions(),v2->getDimensions());

		for(int i=0;i<last;i++) {
			//cout << "V1: " << v1->getCoef(i) << " V2: " << v2->getCoef(i) << endl;
			result += v1->getCoef(i) * v2->getCoef(i);
			if(v1->getCoef(i)==0)
			{
				result =0;
				break;
			}
		}
	//cout << "Resposta do (.)prod foi: " << result << endl;
	return result;
}

double VectorModeler::normFactor(DocVector* v1, DocVector* v2)
{
	double result =0;
	double sum1 =0;
	double sum2 =0;
	int last = max(v1->getDimensions(),v2->getDimensions());
	
			for(int i=0;i<last;i++)
			{
				if(i< v1->getDimensions()) {
					sum1 += v1->getCoef(i) * v1->getCoef(i);
				}
				
				sum2 += v2->getCoef(i) * v2->getCoef(i);
				//cout << "V1: " << v1->getCoef(i) << " V2: " << v2->getCoef(i) << endl;
			}
			sum1 = sqrt(sum1);
			sum2 = sqrt(sum2);
			result = sum1*sum2;

	if(result ==0)
		result = 0.000001;   // Avoid divisions per ZERO!
	
	//cout << "Resposta da norma foi: " << result << endl;
	return result;
}

map<string,double> VectorModeler::calculateScores(map<string,DocVector>* linearSystem,DocVector* Query)
{
	map<string,double> result;
	map<string,DocVector>::iterator it;
	for(it = linearSystem->begin();it!=linearSystem->end();it++)
	{
		//cout << "it->first = " << it->first << endl;
		double score = dotProduct(&(it->second),Query)/normFactor(&(it->second),Query);
		//cout << "result de it->first = " << result[it->first] << endl;
		if(score > 0.8)			
		{
			result[it->first] = score;
			string url = MD5toURL::translate(it->first);
			if(url != "Call Huston")
				cout << "Documento: " << MD5toURL::translate(it->first) << " Valor final: " << result[it->first] <<endl;
		}	
	}
	return result;
}

map<string,double> VectorModeler::applyVectorModel(vector<uint>* wordlist, vector<ResultMap>* allResults)
{
			
			//Agora, para cada palavra, passamos pela sua lista de documentos e vamos a cada
			// passo construindo o DocVector para cada documento. O DocVector contem os 
			// coeficientes necessarios para aplicar o modelo vetorial. No final, armazenamos
			// o sistema linear num vetor de DocVectors;		
			uint NDocs = 3000000;
			map<string,DocVector> linearSystem;
			DocVector Query;
			Query.addNullUpTo(wordlist->size());
			for(int i=0;i<allResults->size();i++) // Para cada palavra
			{
				map<string,DocMatch> doc = allResults->at(i).result; // Obtem seu mapa de resultados
				double Idf;
				if(doc.size()!=0)
					Idf = log(NDocs/doc.size());
				else
					Idf = 0;			
				Query.setPos(i,Idf);
				//cout << "Pushed IDF: " << Idf << " for word: " << allResults[i].getWord() << endl;
				map<string,DocMatch>::iterator it; // Cria um iterator pra passar por cada doc
				for(it=doc.begin();it!=doc.end();it++)
				{
					if(linearSystem.find(it->first)==linearSystem.end()) // Does the lS already contains this doc?
					{
						DocVector v;		// Create a new DocVector and fill it with 0 for previous words
						v.setDoc(it->first);
						v.addNullUpTo(wordlist->size());
						linearSystem[it->first]=v;
					}
					linearSystem[it->first].addWord(i,&(allResults->at(i)));
				}
			}
			
			// Nesse instante, ja temos montados os vetores para cada documento, e
			// o vetor correspondente de busca.
			map<string,double> preRank;
			preRank = VectorModeler::calculateScores(&linearSystem,&Query);
			cout << "Sai do CalculateScores" << endl;
			return preRank;
}